class FARule:
    def __init__(self, state, character, next_state):
        self.state = state
        self.character = character
        self.next_state = next_state

    def applies_to(self, state, character):
        return self.state == state and self.character == character

    def follow(self):
        return self.next_state

    def __repr__(self):
        return f"<FARule {repr(self.state)}--{self.character} -->{repr(self.next_state)}>"

class FARulebook:
    def __init__(self, rules):
        self.rules = rules

    def next_state(self, state, character):
        rule = self.rule_for(state, character)
        if rule:
            return rule.follow()
        raise ValueError(f"No rule defined for state {state} and character {character}")

    def rule_for(self, state, character):
        for rule in self.rules:
            if rule.applies_to(state, character):
                return rule
        return None
    
    def __repr__(self):
        return f"<struct FARulebook ...>"
    
# rulebook = FARulebook(
#     [
#     FARule(1,'a',2), FARule(1,'b',1),
#     FARule(2,'a',2), FARule(2,'b',3),
#     FARule(3,'a',3), FARule(3,'b',3)
#     ]
# )
# print(rulebook)
# print(rulebook.next_state(1,'a'))
# print(rulebook.next_state(1,'b'))
# print(rulebook.next_state(2,'b'))


class DFA:
    def __init__(self, current_state, accept_states, rulebook):
        self.current_state = current_state
        self.accept_states = accept_states
        self.rulebook = rulebook

    def is_accepting(self):
        return self.current_state in self.accept_states
    
    def read_character(self, character):
        self.current_state = rulebook.next_state(self.current_state, character)

    def read_string(self, string):
        for character in string:
            self.read_character(character)


class DFADesign:
    def __init__(self, start_state, accept_states, rulebook):
        self.start_state = start_state
        self.accept_states = accept_states
        self.rulebook = rulebook

    def to_dfa(self):
        return DFA(self.start_state, self.accept_states, self.rulebook)
    
    def accepts(self, string):
        dfa = self.to_dfa()
        dfa.read_string(string)
        return dfa.is_accepting()
    
if __name__ == "__main__":
    rulebook = FARulebook(
        [
        FARule(1,'a',2), FARule(1,'b',1),
        FARule(2,'a',2), FARule(2,'b',3),
        FARule(3,'a',3), FARule(3,'b',3)
        ]
    )
    # print(DFA(1,[1,3], rulebook).is_accepting())
    # print(DFA(1,[3], rulebook).is_accepting())

    # 测试 read_character
    # dfa = DFA(1,[3],rulebook)
    # print(dfa.is_accepting())

    # dfa.read_character('b')
    # print(dfa.is_accepting())

    # for __ in range(3):
    #     dfa.read_character('a')
    # print(dfa.is_accepting())

    # dfa.read_character('b')
    # print(dfa.is_accepting())


    # 测试 read_string
    # dfa = DFA(1,[3],rulebook)
    # print(dfa.is_accepting())

    # dfa.read_string('baaab')
    # print(dfa.is_accepting())

    dfa_design = DFADesign(1,[3],rulebook)
    print(dfa_design.accepts('a'))
    print(dfa_design.accepts('baa'))
    print(dfa_design.accepts('baba'))




